#ifndef __GRAVITY_H__
#define __GRAVITY_H__

#include <list>
#include "../objects/rectangle.h"
#include <cmath>

class Solid{
	private:
		float dropSpeed;
		Rectangle size;
		//bool floor;
	public:
		Solid(Rectangle size,bool floor=false){
			this->dropSpeed = 0;
			this->size = size;
			//this->floor = floor;
		}

		virtual ~Solid(){
		}

		//bool isFloor(){ return floor;  }

		float getDropSpeed(){ return dropSpeed; }

		virtual void accelerate(float accel){
			//if(!floor)
				this->dropSpeed += accel;
		}

		void advanceTime(){
			this->size.translate(0.0f,this->dropSpeed);
		}

		Rectangle motionRectangle(){
			Rectangle advanced(size);
			advanced.translate(0.0f,this->dropSpeed);
			return Rectangle(size.x1,size.x2,size.y1,advanced.y2);
		}

		virtual void notifyCollision(Solid* obj)=0;

		Rectangle& getSize(){ return this->size; }
		void setSize(Rectangle rec){ this->size = rec; }

		bool operator<(const Solid& solid)const{
			if( this->size.y2 == solid.size.y2)
				return (this->size.x1 <  solid.size.x1 );
			return this->size.y2 > solid.size.y2;
		}
};

class PtrSolid{
	private:
		Solid* solid;

	public:
		PtrSolid(Solid* solid){ this->solid=solid; }
		Solid* operator ->(){ return solid; }
		Solid* operator *(){ return solid;  }

		bool operator <(const PtrSolid& ptrSolid)const{
			return (*solid)<(*ptrSolid.solid);
		}

		bool operator ==(const PtrSolid& ptrSolid)const{
			return this->solid==ptrSolid.solid;
		}
};

class Gravity{
	private:
		float gravityAccel;
		std::list<PtrSolid> objects;

	public:
		Gravity(float gravityAccel){
			this->gravityAccel = gravityAccel;
		}

		~Gravity(){
		}

		void clear(){
			objects.clear();
		}

		void addSolid(Solid* solid){
			this->objects.push_back(PtrSolid(solid));
			solid->accelerate(gravityAccel);
			this->objects.sort();
		}

		void removeSolid(Solid* solid){
			this->objects.remove(PtrSolid(solid));
		}

		void advanceTime(){
			std::list<PtrSolid> collided;
			for(std::list<PtrSolid>::iterator it=objects.begin();it!=objects.end();++it){
				std::list<PtrSolid>::iterator it2=objects.begin();

				while( it2!=objects.end()  /*&& !(*it)->isFloor()*/ ){
					bool collide=false;
					if(it!=it2){
						Rectangle mr = (*it)->motionRectangle();
						collide = mr.intersect((*it2)->getSize());

						if(collide){
							collided.push_back(*it2);
						}
					}

					++it2;
				}

				if(!collided.empty()){

					PtrSolid solid = *collided.back();
					if(fabs((*it)->getDropSpeed())>0){
						(*it)->accelerate(-(*it)->getDropSpeed());
						if(solid->getSize().y1>(*it)->getSize().y2){
							(*it)->getSize().translate(0.0f,solid->getSize().y1-(*it)->getSize().y2);
						}
						solid->notifyCollision(**it);
					}
					collided.clear();
				}else{
					(*it)->advanceTime();
					(*it)->accelerate(gravityAccel);
				}

				++it2;
			}
		}
};

#endif // __GRAVITY_H__
